package com.example.administrator.netdemo.network.base;

import android.app.Activity;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import com.cyw.baselibrary.utils.ToastUtils;
import com.example.administrator.netdemo.network.RequestHandler;
import com.example.administrator.netdemo.network.RequestModelManager;
import com.example.administrator.netdemo.network.TokenExpiredException;
import com.jiongbull.jlog.JLog;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Function;
import io.reactivex.functions.Predicate;
import io.reactivex.observers.ResourceObserver;
import io.reactivex.schedulers.Schedulers;
import retrofit2.HttpException;


/**
 * Created by luquan on 2017/3/15.
 */

public class BaseModel {
    protected ApiService apiService;
    protected RequestHandler requestHandler;
    protected Context context;


    public BaseModel(Context context) {
        this.context = context;
        requestHandler = RequestHandler.getInstance();
        apiService = requestHandler.getApiService(context);
//        JLog.d("lq_Request", "addRequest key = " + context.getClass().getName());
        RequestModelManager.getInstance().markReqModel(context.getClass().getName(), this);
    }

    protected void showToast(String tips) {
        ToastUtils.showShort(context, tips);
    }


    protected String getString(int stringId) {
        return context.getString(stringId);
    }


    private String requestTag = "requestTag";

    /**
     * 失败时不会toast后台返回的message，区别于{@link FilterRespStateFunc}
     *
     * @param <T>
     */
    public class FilterRespFunc<T> implements Predicate<HttpResult<T>> {
        private StateFail fail;

        public FilterRespFunc(final StateFail fail) {
            this.fail = fail;
        }

        @Override
        public boolean test(final HttpResult<T> httpResult) {
            final String code = httpResult.getCode();
            if (HttpResult.CODE_SUCCESS.equals(code)) {
                return true;
            } else if (context instanceof Activity) {
                ((Activity) context)
                        .runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Integer state = Integer.parseInt(code);
                                String errMsg = httpResult.getMessage();
                                JLog.e("lq_state_err", state + "," + errMsg);
                                fail.dealFail(state, errMsg);
                            }
                        });
            }
            return false;
        }
    }


    /**
     * 失败时会toast提示用户后台返回的message，区别于 {@link FilterRespFunc}
     *
     * @param <T>
     */
    public class FilterRespStateFunc<T> implements Predicate<HttpResult<T>> {

        private RequestFail fail;

        public FilterRespStateFunc(RequestFail fail) {
            this.fail = fail;
        }

        @Override
        public boolean test(final HttpResult<T> httpResult) {
            String code = httpResult.getCode();
            if (HttpResult.CODE_SUCCESS.equals(code)) {
                return true;
            } else if (context instanceof Activity) {
                ((Activity) context)
                        .runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Integer state = Integer.parseInt(httpResult.getCode());
                                String errMsg = httpResult.getMessage();
                                JLog.e("lq_state_err", state + "," + errMsg);
                                fail.onFail(state);
                                if (!TextUtils.isEmpty(errMsg)) {
                                    showToast(errMsg);
                                }
                            }
                        });

            }
//            }
            return false;
        }

    }

    /**
     * 这里过滤器没有过滤任何东西，直接返回给到上层的是后台返回的全部数据（包含code，message，ata，token等字段）
     *
     * @param <T>
     */
    public class NoFilterFunc<T> implements Predicate<T> {

        private RequestFail fail;

        public NoFilterFunc(RequestFail fail) {
            this.fail = fail;
        }

        public NoFilterFunc() {
        }

        @Override
        public boolean test(final T httpResult) {
            final HttpResult result = (HttpResult) httpResult;
            String code = result.getCode();
            if (HttpResult.CODE_SUCCESS.equals(code)) {
//                return true;
            } else if (context instanceof Activity) {
                ((Activity) context)
                        .runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Integer state = Integer.parseInt(result.getCode());
                                String errMsg = result.getMessage();
                                JLog.e("lq_state_err", state + "," + errMsg);
//                                if (fail != null) {
//                                    fail.onFail(state);
//                                }
                                if (!TextUtils.isEmpty(errMsg)) {
                                    showToast(errMsg);
                                }
                            }
                        });
            }
            //无论结果如何，都执行上层的操作，故都返回true
            return true;
        }

    }

    public class FlatMapRespFuc<T> implements Function<HttpResult<T>, Observable<T>> {

        @Override
        public Observable<T> apply(HttpResult<T> stringHttpResult) {
            if (HttpResult.CODE_LOGIN_DISABLED.equals(stringHttpResult.getCode())) {
//                dealLoginDisable();
                return Observable.error(new TokenExpiredException());
            }
            return Observable.just(stringHttpResult.getData());
        }
    }

    /**
     * 这里过滤器没有过滤任何东西，直接返回给到上层的是后台返回的全部数据（包含code，message，ata，token等字段）
     *
     * @param <T>
     */
    public class MyFlatMapRespFuc<T> implements Function<T, Observable<T>> {

        @Override
        public Observable<T> apply(T stringHttpResult) {
            if (stringHttpResult instanceof HttpResult && HttpResult.CODE_LOGIN_DISABLED.equals(((HttpResult) stringHttpResult).getCode())) {
//                dealLoginDisable();
                return Observable.error(new TokenExpiredException());
            }
            return Observable.just(stringHttpResult);
        }
    }


    protected <T> void toSubscribe(Observable<T> o, ResourceObserver<T> s) {
        Log.d("lq_tag", "requestTag = " + requestTag);
        toSubscribe(o, s, requestTag,2,2);
    }

    protected <T> void toSubscribe(Observable<T> o, ResourceObserver<T> s, Object requestTag) {
        toSubscribe(o, s, requestTag, 2, 2);
    }

    protected <T> void toSubscribe(Observable<T> o, ResourceObserver<T> s, int retryDelaySeconds, int retryCountMax) {
        toSubscribe(o, s, requestTag, retryDelaySeconds, retryCountMax);
    }



    /**
     * @param o
     * @param s
     * @param requestTag:用于取消请求的tag,主要用于特殊的tag，比如这个请求在Activity finish后 不需要取消请求，
     *                                                         在onDestroy，调用cancelAllRequestExceptThisTag
     * @param retryDelaySeconds:发生http错误时重新请求延迟的时间
     * @param retryCountMax：发生http错误重新请求的次数
     * @param <T>
     */
    protected <T> void toSubscribe(Observable<T> o, ResourceObserver<T> s, Object requestTag, int retryDelaySeconds, int retryCountMax) {
        o.subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .retryWhen(new RepeatFunc(retryDelaySeconds, retryCountMax))
                .subscribe(s);

        if (requestTag != null) {
            subscribersMap.put(requestTag, s);
        }
    }

    /**
     * http错误重订阅机制
     */
    public class RepeatFunc implements Function<Observable<? extends Throwable>, Observable<?>> {
        private int retryDelaySeconds;//延迟重试的时间
        private int retryCount;//记录当前重试次数
        private int retryCountMax;//最大重试次数

        public RepeatFunc(int retryDelaySeconds, int retryCountMax) {
            this.retryDelaySeconds = retryDelaySeconds;
            this.retryCountMax = retryCountMax;
        }

        @Override
        public Observable<?> apply(Observable<? extends Throwable> observable) {
            return observable.flatMap(new Function<Throwable, ObservableSource<?>>() {
                @Override
                public Observable<?> apply(Throwable throwable) {
                    if (throwable instanceof HttpException && ++retryCount <= retryCountMax) {
                        JLog.e(MessageFormat.format("HTTP请求出错，尝试再次请求第{0}次", retryCount));
                        return Observable.timer(retryDelaySeconds,
                                TimeUnit.SECONDS);
                    }
                    return Observable.error(throwable);
                }
            });
        }
    }

    private Map<Object, ResourceObserver> subscribersMap = new HashMap();

    public void cancelRequestByTag(Object requestTag) {
        if (subscribersMap != null) {
            ResourceObserver subscriber = subscribersMap.get(requestTag);
            if (subscriber != null && !subscriber.isDisposed()) {
                subscriber.dispose();
                JLog.d("lq_Request", "cancelRequestByTag  = " + requestTag);
            }
        }

    }

    public void cancelAllRequest() {
        for (Object key : subscribersMap.keySet()) {
            cancelRequestByTag(key);
        }
        subscribersMap.clear();
    }

    public void cancelAllRequestExceptThisTag(Object specialTag) {
        for (Object key : subscribersMap.keySet()) {
            if (!key.equals(specialTag)) {
                cancelRequestByTag(key);
            }
        }
        subscribersMap.clear();
    }

    public void cancelAllRequestExceptThoseTags(Object... specialTags) {
        for (Object key : subscribersMap.keySet()) {
            boolean findSpecialTag = false;
            for (Object specialTag : specialTags) {
                if (key.equals(specialTag)) {
                    findSpecialTag = true;
                    break;
                }
            }
            if (!findSpecialTag) {
                cancelRequestByTag(key);
            }
        }
        subscribersMap.clear();
    }


    public interface StateFail {
        void dealFail(int state, String msg);
    }

    public HashMap<String, String> getCommonParams() {
        HashMap<String, String> map = new HashMap<>();
        return map;
    }


}
